//
//  MHAVCapture.m
//  JadeShop
//
//  Created by mac on 2019/7/19.
//  Copyright © 2019年 SZJ.test. All rights reserved.
//

#import "MHAVCapture.h"
#import <AVFoundation/AVFoundation.h>

@interface MHAVCapture()<AVCaptureVideoDataOutputSampleBufferDelegate>
@property (nonatomic,strong) AVCaptureDeviceInput * captureDeviceInput;//输入源
@property (nonatomic,strong) AVCaptureVideoDataOutput * captureVideoDataOutput;//输出源
@property (nonatomic,strong) AVCaptureSession * captureSession;//抽象类  管理者

@property (nonatomic,strong) AVCaptureVideoPreviewLayer * videoPreviewLayer;//视频预览图层
/*
   AVCaptureConnection 输入对象 和 输出对象创建的链接
 */
@property (nonatomic,strong) AVCaptureConnection * captureConnection;

@property (nonatomic,assign,getter=isCapturing) BOOL capturing;
@end

@implementation MHAVCapture

- (BOOL)startCapture{
    if (self.isCapturing)return NO;
    // 摄像头权限判断
    AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if (videoAuthStatus != AVAuthorizationStatusAuthorized)
    {
        return NO;
    }
    [self.captureSession startRunning];
    self.capturing = YES;
    return YES;
}

- (void)changePosition{
    // 获取所有摄像头
    NSArray *cameras= [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    // 获取当前摄像头方向
    AVCaptureDevicePosition currentPosition = self.captureDeviceInput.device.position;
    //初始化新的方向
    AVCaptureDevicePosition toPosition = AVCaptureDevicePositionUnspecified;
    //如果是后置摄像头或者未设置方向  使用前置摄像头
    if (currentPosition == AVCaptureDevicePositionBack || currentPosition == AVCaptureDevicePositionUnspecified)
    {
        toPosition = AVCaptureDevicePositionFront;
    }
    else
    {
        toPosition = AVCaptureDevicePositionBack;
    }
    NSArray *captureDeviceArray = [cameras filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"position == %d", toPosition]];
    if (captureDeviceArray.count == 0)
    {
        NSLog(@"获取摄像头失败");
        return;
    }
    NSError *error = nil;
    AVCaptureDevice *camera = captureDeviceArray.firstObject;
    //开始配置
    [self.captureSession beginConfiguration];
    AVCaptureDeviceInput *newInput = [AVCaptureDeviceInput deviceInputWithDevice:camera error:&error];
    [self.captureSession removeInput:self.captureDeviceInput];//移除旧的input
    if ([_captureSession canAddInput:newInput])
    {
        [_captureSession addInput:newInput];
        self.captureDeviceInput = newInput;
    }
    // 提交配置
    [self.captureSession commitConfiguration];
    // 重新获取连接并设置视频的方向、是否镜像
    [self detailMirrored:camera];
}


- (void)setFrameRate:(NSInteger)frameRate{
    // 获取设置支持设置的帧率范围
    AVFrameRateRange *frameRateRange = [self.captureDeviceInput.device.activeFormat.videoSupportedFrameRateRanges objectAtIndex:0];
    
    if (frameRate > frameRateRange.maxFrameRate || frameRate < frameRateRange.minFrameRate)
    {
        NSLog(@"超出帧率范围");
        return;
    }
    // 设置输入的帧率
    self.captureDeviceInput.device.activeVideoMinFrameDuration = CMTimeMake(1, (int)frameRate);
    self.captureDeviceInput.device.activeVideoMaxFrameDuration = CMTimeMake(1, (int)frameRate);
}

- (void)beginSession{
    
    
    //获取所有摄像头
    NSArray *cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    
    NSArray *captureDeviceArray = [cameras filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"position == %d", AVCaptureDevicePositionFront]];
    if (!captureDeviceArray.count) {
        NSLog(@"获取前置摄像头失败");
        return;
    }
    AVCaptureDevice *camera = captureDeviceArray.firstObject;
    NSError *error;
    self.captureDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:camera error:&error];
    if (error) {
        NSLog(@"AVCaptureDevice转AVCaptureDeviceInput失败");
        return;
    }
    
    self.captureVideoDataOutput = [AVCaptureVideoDataOutput new];
    NSDictionary *videoSet = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange], kCVPixelBufferPixelFormatTypeKey, nil];
    [self.captureVideoDataOutput setVideoSettings:videoSet];
    //串行队列
    dispatch_queue_t outputQueue = dispatch_queue_create("ACVideoCaptureOutputQueue", DISPATCH_QUEUE_SERIAL);
    [self.captureVideoDataOutput setSampleBufferDelegate:self queue:outputQueue];
    //丢弃延迟的帧
    self.captureVideoDataOutput.alwaysDiscardsLateVideoFrames = YES;
    
    self.captureSession = [AVCaptureSession new];
    //不使用应用实例 避免被挂断
    self.captureSession.usesApplicationAudioSession = NO;
    
    /*
     如果在已经开启采集的情况下需要修改分辨率或输入输出，需要用beginConfiguration和commitConfiguration把修改的代码包围起来。在调用beginConfiguration后，可以配置分辨率、输入输出等，直到调用commitConfiguration了才会被应用。
     */

    //添加输入设备
    if ([self.captureSession canAddInput:self.captureDeviceInput]) {
        [self.captureSession addInput:self.captureDeviceInput];
    }
    if ([self.captureSession canAddOutput:self.captureVideoDataOutput]) {
        [self.captureSession addOutput:self.captureVideoDataOutput];
    }
    //设置分辨率
    if ([self.captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
        [self.captureSession setSessionPreset:AVCaptureSessionPreset1280x720];
    }
    
    [self detailMirrored:camera];
    
    // 获取预览Layer并设置视频方向，注意self.videoPreviewLayer.connection跟self.captureConnection不是同一个对象，要分开设置
    self.videoPreviewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
    self.videoPreviewLayer.connection.videoOrientation = self.captureConnection.videoOrientation;
    /*
     AVlayerVideoGravityResize
     
     指定应拉伸视频以填充层的边界。
     
     AVlayerVideoGravityResizeAspect
     
     指定播放机应保留视频的纵横比并使视频适合层边界。
     
     AVlayerVideoGravityResizeAspectFill
     
     指定播放机应保留视频的纵横比并填充层的边界。
     */
    self.videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
}

- (void)detailMirrored:(AVCaptureDevice *)camera{
    self.captureConnection = [self.captureVideoDataOutput connectionWithMediaType:AVMediaTypeVideo];
    /*
     设置视频方向
     AVCaptureVideoOrientationPortrait           = 1, 指示视频应垂直放置，顶部位于顶部。
     AVCaptureVideoOrientationPortraitUpsideDown = 2, 指示视频应垂直定向，顶部位于底部。
     AVCaptureVideoOrientationLandscapeRight     = 3, 指示视频应水平放置，顶部在左侧。
     AVCaptureVideoOrientationLandscapeLeft  指示视频应水平放置，顶部在右侧。
     */
    self.captureConnection.videoOrientation = AVCaptureVideoOrientationPortrait;
    // 设置是否为镜像，前置摄像头采集到的数据本来就是翻转的，这里设置为镜像把画面转回来
    /*
     typedef NS_ENUM(NSInteger, AVCaptureDevicePosition) {
     AVCaptureDevicePositionUnspecified = 0, //未确定
     AVCaptureDevicePositionBack        = 1, //后置摄像头
     AVCaptureDevicePositionFront       = 2, //前置摄像头
     } NS_AVAILABLE(10_7, 4_0) __TVOS_PROHIBITED;
     */
    if (camera.position == AVCaptureDevicePositionFront && self.captureConnection.supportsVideoMirroring) {
        self.captureConnection.videoMirrored = YES;
    }
}

//每当输出捕获并输出新的视频帧、根据其VideoSettings属性对其进行解码或重新编码时，代理都会收到此消息。
- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{
    
}

//视频帧丢失回调
- (void)captureOutput:(AVCaptureOutput *)output didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{
    
}

@end
